package mycdm

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"time"
)

type Exectab struct {
	Path        string            `json:"path"`
	Name        string            `json:"name"`
	Args        []string          `json:"args"`
	Evn         map[string]string `json:"env"`
	Dir         string            `json:"dir"`
	Stoptimeout time.Duration     `json:"stoptimeout`
	stdin       io.Reader
	stdout      io.Writer
	stderr      io.Writer
	log         *log.Logger
}

type Instence struct {
	Exe     *Exectab `json:"config"`
	Running bool     `json:"status"`
	cancel  func()
}

func ReginInstence(conf *Exectab) *Instence {
	return &Instence{Exe: conf}
}

func (e *Exectab) stopkill(CMD *exec.Cmd, res <-chan error) error {
	e.log.Println("Sending SIGINT")
	err := CMD.Process.Signal(os.Interrupt)
	if err != nil {
		e.log.Println("Failed send SIGINT:", err)
	}
	select {
	case err = <-res:
		e.log.Println("Process gracefull stopped")
	case <-time.After(e.StopTimeout):
		e.log.Println("Process gracefull shutdown waiting timeout")
		err = CMD.Process.Kill()
	}
	return err
}

func (e *Exectab) execfun(ctx context.Context) error {
	defer fmt.Printf("job:%s completed!\n", e.Name)
	cmd := exec.Command(e.Name)
	// 设置命令参数
	if e.Dir != "" {
		cmd.Dir = e.Dir
	}
	if e.Path != "" {
		cmd.Path = e.Path
	}
	if e.Args != nil {
		cmd.Args = e.Args
	}
	if e.stdin != nil {
		cmd.Stdin = e.stdin
	}
	if e.stdout != nil {
		cmd.Stdout = e.stdout
	}
	if e.stderr != nil {
		cmd.Stderr = e.stderr
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	var (
		res = make(chan error, 1)
		err error
	)
	go func() { res <- cmd.Wait() }()
	// 双向选择：
	select {
	// 1.终断停止函数并且kill进程
	case <-ctx.Done():
		err = e.stopkill(cmd, res)
	// 2.进程停止退出函数
	case err = <-res:
	}
	return err
}

func (I *Instence) Run(Ctx context.Context) {
	ctx, cancel := context.WithCancel(Ctx)
	I.cancel = cancel
	I.Running = true
	defer func() { I.Running = false }()
	err := I.Exe.execfun(ctx)
	if err != nil {
		fmt.Println(err)
	}
}

func (I *Instence) Stop() {
	I.cancel()
}
